<?php
/**
 * TinyKan+ – 单文件任务看板（登录 + 日志 + 优先级 + 双卡片并排）
 * 依赖：PHP≥7.4 且启用 pdo_sqlite
 * 用法：php -S localhost:8000 index.php
 */
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');      // 北京时间
session_start();

/* ---------- 0. 基础配置 ---------- */
$DB_FILE   = __DIR__ . '/tinykan.db';
$CSV_NAME  = 'tinykan_export.csv';

/* ---------- 1. 初始化/升级数据库 ---------- */
function db(array $sqls = []): PDO
{
    global $DB_FILE;
    $pdo = new PDO("sqlite:{$DB_FILE}");
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    foreach ($sqls as $sql) {
        $pdo->exec($sql);
    }
    return $pdo;
}

/* === 建表 === */
db([
    "CREATE TABLE IF NOT EXISTS user (
        id       INTEGER PRIMARY KEY AUTOINCREMENT,
        email    TEXT UNIQUE,
        name     TEXT,
        password TEXT,
        created  TEXT DEFAULT (datetime('now','localtime'))
    )",
    "CREATE TABLE IF NOT EXISTS task (
        id          INTEGER PRIMARY KEY AUTOINCREMENT,
        title       TEXT NOT NULL,
        body        TEXT,
        assignee    TEXT,
        due         TEXT,
        color       TEXT,
        status      INTEGER CHECK(status IN (0,1,2)) DEFAULT 0,
        archived    INTEGER DEFAULT 0,
        last_user_id INTEGER,
        created     TEXT DEFAULT (datetime('now','localtime'))
    )",
    "CREATE TABLE IF NOT EXISTS log (
        id      INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER,
        task_id INTEGER,
        action  TEXT,
        created TEXT DEFAULT (datetime('now','localtime'))
    )"
]);

/* === 优先级字段：不存在才加 === */
$pdo = db();
if (!$pdo->query("SELECT 1 FROM pragma_table_info('task') WHERE name='priority'")->fetch()) {
    $pdo->exec("ALTER TABLE task ADD COLUMN priority INTEGER CHECK(priority IN (0,1,2)) DEFAULT 1");
}


/* ---------- 2. 当前登录用户 ---------- */
function cur_user(): ?array
{
    if (empty($_SESSION['uid'])) return null;
    $pdo = db();
    $st  = $pdo->prepare('SELECT * FROM user WHERE id=?');
    $st->execute([$_SESSION['uid']]);
    return $st->fetch(PDO::FETCH_ASSOC) ?: null;
}
function login_check(): void
{
    if (!cur_user()) {
        header('Location: ?a=login'); exit;
    }
}

/* ---------- 3. 写日志 ---------- */
function log_action(int $task_id, string $action): void
{
    $user = cur_user();
    if (!$user) return;
    $pdo = db();
    $st  = $pdo->prepare('INSERT INTO log(user_id,task_id,action) VALUES (?,?,?)');
    $st->execute([$user['id'], $task_id, $action]);
}

/* ---------- 4. 路由 ---------- */
$a = $_GET['a'] ?? 'board';
switch ($a) {
case 'register': handle_register(); break;
case 'login':    handle_login();    break;
case 'logout':   session_destroy(); header('Location: ?a=login'); exit;
default:         login_check(); handle_board(); break;
}

/* ---------- 5. 注册 ---------- */
function handle_register(): void
{
    if ($_SERVER['REQUEST_METHOD']==='POST' && !empty($_POST['email'])) {
        $email = trim($_POST['email']);
        $name  = trim($_POST['name']) ?: $email;
        $pwd   = password_hash(trim($_POST['password']), PASSWORD_BCRYPT);
        $pdo   = db();
        try {
            $pdo->prepare('INSERT INTO user(email,name,password) VALUES (?,?,?)')
                ->execute([$email,$name,$pwd]);
            $_SESSION['uid'] = $pdo->lastInsertId();
            header('Location: ?a=board'); exit;
        } catch (PDOException $e) {
            $err = '邮箱已存在';
        }
    }
    ?>
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>注册 - TinyKan</title>
<style>*{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto;font-size:14px}body{background:#f7f7f7;display:flex;height:100vh;align-items:center;justify-content:center}.box{background:#fff;padding:30px;border-radius:6px;width:320px;box-shadow:0 1px 4px rgba(0,0,0,.1)}h3{margin:0 0 15px}input{width:100%;padding:8px;margin:6px 0;border:1px solid #ccc;border-radius:4px}button{background:#1976d2;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;width:100%}.err{color:#d32f2f;margin-bottom:8px}</style>
</head><body>
<div class="box"><h3>注册</h3>
<?php if(!empty($err)) echo "<div class=err>$err</div>"; ?>
<form method="post">
<input name="email" type="email" placeholder="邮箱" required>
<input name="name" placeholder="昵称（可空）">
<input name="password" type="password" placeholder="密码" minlength="4" required>
<button>注册并登录</button>
</form>
<p style="text-align:center;margin-top:15px"><a href="?a=login">已有账号？登录</a></p>
</div></body></html>
    <?php exit;
}

/* ---------- 6. 登录 ---------- */
function handle_login(): void
{
    if ($_SERVER['REQUEST_METHOD']==='POST' && !empty($_POST['email'])) {
        $email = trim($_POST['email']);
        $pwd   = trim($_POST['password']);
        $pdo   = db();
        $st    = $pdo->prepare('SELECT * FROM user WHERE email=?');
        $st->execute([$email]);
        $u = $st->fetch(PDO::FETCH_ASSOC);
        if ($u && password_verify($pwd, $u['password'])) {
            $_SESSION['uid'] = $u['id'];
            header('Location: ?a=board'); exit;
        }
        $err = '邮箱或密码错误';
    }
    ?>
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>登录 - TinyKan</title>
<style>*{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto;font-size:14px}body{background:#f7f7f7;display:flex;height:100vh;align-items:center;justify-content:center}.box{background:#fff;padding:30px;border-radius:6px;width:320px;box-shadow:0 1px 4px rgba(0,0,0,.1)}h3{margin:0 0 15px}input{width:100%;padding:8px;margin:6px 0;border:1px solid #ccc;border-radius:4px}button{background:#1976d2;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;width:100%}.err{color:#d32f2f;margin-bottom:8px}</style>
</head><body>
<div class="box"><h3>登录</h3>
<?php if(!empty($err)) echo "<div class=err>$err</div>"; ?>
<form method="post">
<input name="email" type="email" placeholder="邮箱" required>
<input name="password" type="password" placeholder="密码" required>
<button>登录</button>
</form>
<p style="text-align:center;margin-top:15px"><a href="?a=register">没有账号？注册</a></p>
</div></body></html>
    <?php exit;
}

/* ---------- 7. 看板与操作 ---------- */
function handle_board(): void
{
    $pdo = db();
    $user = cur_user();

    /* ---- 7.1 处理动作 ---- */
    $action = $_POST['action'] ?? '';
    switch ($action) {
    case 'add':
    case 'update':
        $id       = (int)($_POST['id'] ?? 0);
        $title    = trim($_POST['title'] ?? '');
        $body     = trim($_POST['body'] ?? '');
        $assignee = trim($_POST['assignee'] ?? '');
        $due      = trim($_POST['due'] ?? '');
        $color    = trim($_POST['color'] ?? '#ffffff');
        $status   = (int)($_POST['status'] ?? 0);
        $priority = (int)($_POST['priority'] ?? 1);
        if ($title === '') break;
        $now = date('Y-m-d H:i:s');
        if ($id) {
            $st = $pdo->prepare('UPDATE task SET title=?,body=?,assignee=?,due=?,color=?,status=?,last_user_id=?,priority=? WHERE id=?');
            $st->execute([$title,$body,$assignee,$due,$color,$status,$user['id'],$priority,$id]);
            log_action($id,'update');
        } else {
            $st = $pdo->prepare('INSERT INTO task (title,body,assignee,due,color,status,last_user_id,created,priority) VALUES (?,?,?,?,?,?,?,?,?)');
            $st->execute([$title,$body,$assignee,$due,$color,$status,$user['id'],$now,$priority]);
            log_action((int)$pdo->lastInsertId(),'add');
        }
        header('Location: ?a=board'); exit;
    case 'archive':
        $id = (int)($_POST['id'] ?? 0);
        $pdo->prepare('UPDATE task SET archived=1 WHERE id=?')->execute([$id]);
        log_action($id,'archive');
        header('Location: ?a=board'); exit;
    case 'export':
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="'.$GLOBALS['CSV_NAME'].'"');
        $out = fopen('php://output','w');
        fputcsv($out,['ID','标题','描述','负责人','截止日','颜色','状态','优先级','创建时间','最后操作人']);
        foreach ($pdo->query('SELECT t.*,u.name as op_name FROM task t LEFT JOIN user u ON t.last_user_id=u.id WHERE t.archived=0 ORDER BY t.id') as $r) {
            fputcsv($out,[$r['id'],$r['title'],$r['body'],$r['assignee'],$r['due'],$r['color'],$r['status'],$r['priority'],$r['created'],$r['op_name']]);
        }
        fclose($out); exit;
    }

    /* ---- 7.2 读取任务 ---- */
    $tasks = [0=>[],1=>[],2=>[]]; $archived = [];
    $sql = 'SELECT t.*,u.name as op_name
            FROM task t
            LEFT JOIN user u ON t.last_user_id=u.id
            WHERE t.archived=0
            ORDER BY t.status, t.priority DESC, t.id DESC';
    foreach ($pdo->query($sql) as $t) {
        $tasks[$t['status']][] = $t;
    }
    foreach ($pdo->query('SELECT t.*,u.name as op_name FROM task t LEFT JOIN user u ON t.last_user_id=u.id WHERE t.archived=1 ORDER BY t.id DESC') as $t) {
        $archived[] = $t;
    }

    /* ---- 7.3 读取日志 ---- */
    $logs = [];
    foreach ($pdo->query('SELECT l.*,u.name,u.email FROM log l JOIN user u ON l.user_id=u.id ORDER BY l.id DESC LIMIT 50') as $l) {
        $logs[] = $l;
    }
    ?>
<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><title>TinyKan+ 看板</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
/* ---------- 全局 ---------- */
*{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial;font-size:13px}
body{margin:0;background:#f7f7f7;color:#333}
h1{margin:8px 12px;font-size:1.3em}

/* ---------- 看板：三列强制等宽 ---------- */
.board{display:flex;gap:10px;padding:10px;overflow-x:auto}
.column{
    flex:1 1 0%;
    width:0;
    background:#fff;
    border-radius:4px;
    padding:8px;
    min-height:360px;
    box-shadow:0 1px 2px rgba(0,0,0,.08);
    display:flex;              /* 双卡片关键 */
    flex-wrap:wrap;
    gap:6px;
    align-content:flex-start;
}
.column h3{
    width:100%;                  /* 标题独占一行 */
    margin:0 0 6px 0;
    font-size:1em;font-weight:600
}

/* ---------- 任务卡片 ---------- */
.task{
    width:calc(50% - 3px);       /* 一行两张 */
    margin:0 !important;
    background:#fafafa;
    border:1px solid #e0e0e0;
    border-radius:3px;
    padding:5px 6px 4px;
    cursor:grab;
    position:relative;
    line-height:1.35;
    overflow-wrap:break-word;
    word-break:break-word;
    hyphens:auto;
}
.task:hover{border-color:#999}
.task-title{font-weight:600;font-size:12px;margin-bottom:2px}
.task-body{font-size:11px;color:#444;margin:2px 0}
.task-meta{font-size:10px;color:#666}
.task-actions{margin-top:4px;text-align:right;font-size:10px}
.task-actions a{color:#1976d2;text-decoration:none;margin-left:4px}
.add-btn{width:calc(50% - 3px);margin:0 !important;display:inline-block;font-size:11px;color:#1976d2;cursor:pointer}

/* ---------- 优先级小标签 ---------- */
.prio-tag{
  float:left;
  margin:-2px 4px 0 0;
  padding:1px 4px;
  border-radius:3px;
  color:#fff;
  font-size:10px;
  line-height:1;
}

/* ---------- 内联表单 ---------- */
form.inline-form{display:none;margin-top:6px;width:100%}
input,textarea,select{width:100%;padding:4px 5px;margin:2px 0;border:1px solid #ccc;border-radius:2px;font-size:11px}
button.btn-primary{background:#1976d2;color:#fff;border:none;padding:3px 6px;border-radius:2px;cursor:pointer;font-size:11px}
button.btn-primary:hover{background:#1565c0}

/* ---------- 归档 & 日志 ---------- */
.archived-box,.log-box{margin:10px;background:#fff;border-radius:4px;padding:8px;font-size:11px}
details summary{cursor:pointer;font-weight:600;margin-bottom:4px}
.export-btn{float:right;margin:10px;font-size:11px}
.userinfo{position:absolute;right:12px;top:10px;font-size:11px}
.userinfo a{color:#1976d2;margin-left:6px}
.log-list{font-size:10px;max-height:180px;overflow:auto}
.log-item{padding:2px 0;border-bottom:1px solid #f0f0f0}

/* 快速状态按钮 */
.quick-status{display:flex;gap:4px;margin:0 0 4px 0;align-items:center}
.quick-status button{
  padding:2px 5px;font-size:10px;border:1px solid #ccc;border-radius:2px;background:#fff;cursor:pointer
}
.quick-status button:disabled{
  background:#1976d2;color:#fff;border-color:#1976d2;cursor:default;opacity:.8
}
</style>
</head>
<body>
<div class="userinfo">👤 <?= htmlspecialchars($user['name']) ?> <a href="?a=logout">退出</a></div>
<h1>日常看板</h1>
<form method="post" style="display:inline;"><button class="export-btn" name="action" value="export">导出 CSV</button></form>

<div class="board">
<?php foreach ($tasks as $status => $list): ?>
<div class="column" data-status="<?= $status ?>">
<h3><?= [0=>'待办',1=>'进行中',2=>'完成'][$status] ?> (<?= count($list) ?>)</h3>
<?php
$prioColor = ['#9e9e9e','#ff9800','#f44336'];
$prioText  = ['低','中','高'];
foreach ($list as $t):
  $last = $t['op_name'] ? ($t['op_name'].' · '.$t['created']) : $t['created'];
?>
<div class="task" style="border-left:4px solid <?= htmlspecialchars($t['color']) ?>;">
  <span class="prio-tag" style="background:<?= $prioColor[$t['priority']] ?>"><?= $prioText[$t['priority']] ?></span>
  <form method="post" class="quick-status">
    <input type="hidden" name="id" value="<?= $t['id'] ?>">
    <input type="hidden" name="title" value="<?= htmlspecialchars($t['title']) ?>">
    <input type="hidden" name="body" value="<?= htmlspecialchars($t['body']) ?>">
    <input type="hidden" name="assignee" value="<?= htmlspecialchars($t['assignee']) ?>">
    <input type="hidden" name="due" value="<?= htmlspecialchars($t['due']) ?>">
    <input type="hidden" name="color" value="<?= htmlspecialchars($t['color']) ?>">
    <input type="hidden" name="priority" value="<?= $t['priority'] ?>">
    <input type="hidden" name="action" value="update">

    <button name="status" value="0" <?= $t['status']==0?'disabled':'' ?>>待办</button>
    <button name="status" value="1" <?= $t['status']==1?'disabled':'' ?>>进行中</button>
    <button name="status" value="2" <?= $t['status']==2?'disabled':'' ?>>完成</button>

    <a href="javascript:void(0)" onclick="toggleForm(<?= $t['id'] ?>)" style="margin-left:auto;font-size:11px;cursor:pointer">编辑</a>
    <form method="post" style="display:inline;" onsubmit="return confirm('归档？')">
      <input type="hidden" name="id" value="<?= $t['id'] ?>">
      <button name="action" value="archive" style="background:none;border:none;color:#d32f2f;cursor:pointer;font-size:11px;margin-left:6px;">归档</button>
    </form>
  </form>

  <div class="task-title"><?= htmlspecialchars($t['title']) ?></div>
  <?php if ($t['body']): ?><div class="task-body"><?= nl2br(htmlspecialchars($t['body'])) ?></div><?php endif; ?>
  <div class="task-meta">
    <?php if ($t['assignee']): ?>👤 <?= htmlspecialchars($t['assignee']) ?><?php endif; ?>
    <?php if ($t['due']): ?> 📅 <?= htmlspecialchars($t['due']) ?><?php endif; ?>
    <div>✎ <?= htmlspecialchars($last) ?></div>
  </div>

  <form class="inline-form" id="form-<?= $t['id'] ?>" method="post">
    <input type="hidden" name="id" value="<?= $t['id'] ?>">
    <input name="title" placeholder="标题" value="<?= htmlspecialchars($t['title']) ?>" required>
    <textarea name="body" placeholder="描述" rows="2"><?= htmlspecialchars($t['body']) ?></textarea>
    <input name="assignee" placeholder="负责人" value="<?= htmlspecialchars($t['assignee']) ?>">
    <input name="due" type="date" value="<?= htmlspecialchars($t['due']) ?>">
    <input name="color" type="color" value="<?= htmlspecialchars($t['color']) ?>">
    <select name="status">
      <option value="0" <?= $t['status']==0?'selected':'' ?>>待办</option>
      <option value="1" <?= $t['status']==1?'selected':'' ?>>进行中</option>
      <option value="2" <?= $t['status']==2?'selected':'' ?>>完成</option>
    </select>
    <select name="priority">
      <option value="0" <?= $t['priority']==0?'selected':'' ?>>优先级 - 低</option>
      <option value="1" <?= $t['priority']==1?'selected':'' ?>>优先级 - 中</option>
      <option value="2" <?= $t['priority']==2?'selected':'' ?>>优先级 - 高</option>
    </select>
    <button class="btn-primary" name="action" value="update">保存</button>
    <button type="button" onclick="toggleForm(<?= $t['id'] ?>)">取消</button>
  </form>
</div>
<?php endforeach; ?>
<div class="add-btn" onclick="toggleForm('new<?= $status ?>')">+ 添加任务</div>
<form id="form-new<?= $status ?>" class="inline-form" method="post">
    <input type="hidden" name="status" value="<?= $status ?>">
    <input name="title" placeholder="标题" required>
    <textarea name="body" placeholder="描述" rows="2"></textarea>
    <input name="assignee" placeholder="负责人">
    <input name="due" type="date">
    <input name="color" type="color" value="#ffffff">
    <select name="priority">
      <option value="1">优先级 - 中</option>
      <option value="0">优先级 - 低</option>
      <option value="2">优先级 - 高</option>
    </select>
    <button class="btn-primary" name="action" value="add">添加</button>
    <button type="button" onclick="toggleForm('new<?= $status ?>')">取消</button>
</form>
</div>
<?php endforeach; ?>
</div>

<?php if ($archived): ?>
<details class="archived-box">
  <summary>已归档任务 (<?= count($archived) ?>) </summary>
  <?php
  $prioColor = ['#9e9e9e','#ff9800','#f44336'];
  $prioText  = ['低','中','高'];
  foreach ($archived as $t): ?>
  <div class="task archived" style="opacity:.75;border-left:4px solid <?= htmlspecialchars($t['color']) ?>;">
    <span class="prio-tag" style="background:<?= $prioColor[$t['priority']] ?>"><?= $prioText[$t['priority']] ?></span>
    <div class="task-title"><?= htmlspecialchars($t['title']) ?></div>
    <div class="task-meta">
      👤 <?= htmlspecialchars($t['assignee'] ?: '-') ?>
      · 📅 <?= htmlspecialchars($t['created']) ?>
      <a href="javascript:void(0)" onclick="toggleArchDetail(<?= $t['id'] ?>)" style="float:right;font-size:10px;cursor:pointer">查看详情</a>
    </div>
    <div id="arch-<?= $t['id'] ?>" style="display:none;margin-top:6px;font-size:11px;">
      <b>描述：</b>
      <div style="background:#f5f5f5;padding:4px;border-radius:2px;margin:4px 0;"><?= nl2br(htmlspecialchars($t['body'] ?: '无')) ?></div>
      <b>截止日：</b><?= htmlspecialchars($t['due'] ?: '无') ?><br>
      <b>归档时间：</b><?= htmlspecialchars($t['created']) ?>
    </div>
  </div>
  <?php endforeach; ?>
</details>
<?php endif; ?>

<details class="log-box">
    <summary>🔍 操作记录（最近 50 条）</summary>
    <div class="log-list">
        <?php foreach ($logs as $l): ?>
        <div class="log-item">
            <?= htmlspecialchars($l['name'] . '(' . $l['email'] . ')') ?>
            — <?= $l['action']==='add'?'创建':($l['action']==='update'?'更新':'归档') ?>
            — <?= htmlspecialchars(substr($l['created'],0,16)) ?>
        </div>
        <?php endforeach; ?>
    </div>
</details>

<script>
function toggleForm(id){
    const f = document.getElementById('form-'+id);
    f.style.display = f.style.display==='block'?'none':'block';
}
function toggleArchDetail(id){
  const d=document.getElementById('arch-'+id);
  d.style.display = (d.style.display==='block')?'none':'block';
  return false;
}
</script>
</body></html>
    <?php
}
